<?
/*
Copyright 2009 Allan Psicobyte

sluger es una aplicación de la Oficina de Software Libre (http://osl.ugr.es/) de la Universidad de Granada (http://www.ugr.es/) que permite crear atajos con URLs cortas y redireccionar al usuario a través de ellas (smilar a servicios como tinyurl o ATAJA). 

Es software libre y se distribuye bajo una licencia GPL de la GNU (http://www.gnu.org/copyleft/gpl.html).

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.


VERSION 1.2

Rewriterule ^([^/]{4})$ index.php?modo=go&url=$1
Rewriterule ^stats/([^/]{4})$ index.php?modo=stats&url=$1
Rewriterule ^new/([^/]{4})$ index.php?modo=new&url=$1
Rewriterule ^([^/]{4}[^/]+)$ index.php?modo=goelec&url=$1
*/

define("BD_HOST","localhost");
define("BD_NAME","go4es");
define("BD_USER","root");
define("BD_PASS","");
define("DOMAIN","go4.es");


Init();

if ($_GET["modo"]=='go' && $_GET["url"] != ''){

	if (false && strlen($_GET["url"])>4){
		$url= Buscar_por_id_propia($_GET["url"]);
		if (!preg_match('"^ERROR:"',$url)){
			Logea($_GET["url"]);
			Redirecciona($url);
		}

		else {
			MuestraError($url);
		}

	}
	else {

		$url= Buscar_por_id($_GET["url"]);
		if (!preg_match('"^ERROR:"',$url)){
			Logea($_GET["url"]);
			Redirecciona($url);
		}

		else {
			MuestraError($url);
		}
	}
}



elseif ($_GET["modo"]=='new' ){
	if (FiltraIP()){
		if (TestAutoreferencia($_GET["url"])){
			if ($_GET["myid"]!=''){
		
				$url= CreaPropia($_GET["myid"],$_GET["url"]);
				if (!preg_match('"^ERROR:"',$url)){
					Pantallamuestra($url);
				}
				else {
					MuestraError($url);
				}
			}
			else {	
	
				$url= Crear($_GET["url"]);
				if (!preg_match('"^ERROR:"',$url)){
					Pantallamuestra($url);
				}
				else {
					MuestraError($url);
				}
			}
		}
		else{
			MuestraError('ERROR:autoreferencia');
		}
	}
	
	else {
		MuestraError('ERROR:ip');
	}
}

else {

	if (FiltraIP()){
		PantallaCrea();
	}
	else {
		MuestraError('ERROR:ip');
	}
}



function Init(){
	global $Conexion, $N, $Caracteres;

	$Caracteres= array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');

	$N= count($Caracteres);
        
	$Conexion = mysql_connect(BD_HOST,BD_USER,BD_PASS);
	mysql_select_db (BD_NAME, $Conexion) OR die ("No se puede conectar");
}



//Pequeño validador de URLs
// Solo para ver si tiene el protocolo

function TestURL($url){
	return preg_match('"^(ftp|(http(s)?))://"',$url);
}


// Para evitar el peligro de crear URLs cortas que enlacen a sí mismas
function TestAutoreferencia($url){
	if (preg_match('"go4\.es"',$url)){
		return false;
	}
	else {
		return true;
	}
}


// Para testear que no se usan caracteres no permitidos en las URLs cortas definidas por el usuario.
function TestCaracteres($id){
	return !preg_match('"[^a-z0-9A-Z_]"',$id);
}




// Crea una URL corta automática. El usuario sólo debe indicar la URL destino
function Crear($url){
	global $Conexion;
	if(trim($url)==""){
		return "ERROR:vacío";
	}
	if (!TestURL($url)){
		$url= 'http://'. $url;
	}
	
	$ext="";	
	$partes=getUrlParts($url);
	if((strtolower($partes["site"])==DOMAIN || strtolower($partes["site"])=="www.".DOMAIN)  && ($partes['resource']=="" || $partes['resource']=="/")){
		return "";
	}
	$archivo=$partes['resource'];
	$barra=strrpos($archivo,"/");	
	if($barra!==false){
		$archivo=substr($archivo,$barra+1);
	}
	
	$punto=strrpos($archivo,".");		
	if($punto!==false){
		$ext=substr($archivo,$punto);
		if(strlen($ext)>5){
			$ext="";
		}
		switch(strtolower($ext)){
			case ".jpg":
			case ".jpeg":
			case ".bmp":	
			case ".gif":
			case ".png":
			case ".tif":
			case ".psd":
			case ".xcf":
			case ".avi":
			case ".mpg":
			case ".mpeg":
			case ".swf":
			case ".divx":
			case ".mkv":
			case ".js":
			//No hacemos nada
			break;
			default:
			$ext="";
		}
	}
	
	$escapada= mysql_real_escape_string($url);
	$ext=mysql_real_escape_string($ext);
	$sql="SELECT * FROM direcciones WHERE url='".$escapada."';";
	$result=mysql_query($sql,$Conexion);

	while($row = mysql_fetch_array($result)) {
		$URL_id=$row["codigo"];
	}
	mysql_free_result($result);
	
	if ($URL_id!=''){
		$URL_corta=$URL_id;// Num_Cad($URL_id);
		if (!preg_match('"^ERROR:"',$URL_corta)){
			return $URL_corta;
		}
	}
	else {

		$fecha= date("Y-m-d H:i:s");

		$sql="INSERT INTO direcciones (url, creada) VALUES ('".$escapada."','".$fecha."');";
		$result=mysql_query($sql,$Conexion);
		$SQL_Error=  mysql_error();
		if ($SQL_Error!=''){
			//echo $SQL_Error;
		}
		else {

			$sql="SELECT * FROM direcciones WHERE url='".$escapada."';";
			$result=mysql_query($sql,$Conexion);

			while($row = mysql_fetch_array($result)) {
				$URL_id=$row["id"];
			}
			mysql_free_result($result);

			$URL_corta= Num_Cad($URL_id);
			$URL_corta=$URL_corta.$ext;
			mysql_query("UPDATE direcciones SET codigo='$URL_corta' WHERE id='$URL_id'",$Conexion);
			if (!preg_match('"^ERROR:"',$URL_corta)){
				return $URL_corta;
			}
		}
	}
}



// Pasa de formato numérico a cadena de caracteres, para convertir ids de la BD en cadenas para la URL.
function Num_Cad($num){
	global $N, $Caracteres;

	$N4= pow($N,4);

	if (is_numeric($num) && $num < $N4 && $num > -1){

		$nume= $num;
		$Caracter[0]= $nume % $N;
	
		$nume= ($nume - $Caracter[0])/$N;
		$Caracter[1]= $nume % $N;
	
		$nume= ($nume - $Caracter[1])/$N;
		$Caracter[2]= $nume % $N;
	
		$nume= ($nume - $Caracter[2])/$N;
		$Caracter[3]= $nume % $N;
		
		$resultado= $Caracteres[$Caracter[3]] . $Caracteres[$Caracter[2]] . $Caracteres[$Caracter[1]] . $Caracteres[$Caracter[0]];
	
		return $resultado;
	}
	else {
		return 'ERROR:inválido';
	}
}


// Pasa de  cadena de caracteres a formato numérico, para convertir URLs cortas en ids de la BD.
function Cad_Num($cad){
	global $N, $Caracteres;

	$N2= pow($N,2);
	$N3= pow($N,3);

	$Caracter= str_split($cad);


	$RegExp= implode('', $Caracteres);
	$RegExp= "^[". $RegExp ."]*$";

	if (ereg($RegExp,$cad) && (strlen($cad)==4)){

		
	$Valor[0]= array_keys($Caracteres, $Caracter[0]);
	$Valor[1]= array_keys($Caracteres, $Caracter[1]);
	$Valor[2]= array_keys($Caracteres, $Caracter[2]);
	$Valor[3]= array_keys($Caracteres, $Caracter[3]);

	$numero = $N3 * ($Valor[0][0]) + $N2 * ($Valor[1][0]) + $N * ($Valor[2][0]) + $Valor[3][0];


		return $numero;

	}
	else {
		return 'ERROR:inválido';
	}
}


// Busca una URL en la BD (automáticas)
function Muestra($id){
	global $Conexion;

	$escapada= mysql_real_escape_string($id);
 
	$sql="SELECT * FROM direcciones WHERE id='".$escapada."';";
	$result=mysql_query($sql,$Conexion);

	while($row = mysql_fetch_array($result)) {
		$url=$row["url"];
	}
	mysql_free_result($result);
	
	if ($url!=''){
		$URL_corta= Num_Cad($URL_id);

		if (!preg_match('"^ERROR:"',$URL_corta)){
			return $URL_corta;
		}

	}
	else {
		return 'ERROR:noexiste';
	}
}


// Busca una URL en la BD (automáticas)
function Buscar_por_id($id){
	global $Conexion;

	
	$escapada= mysql_real_escape_string($id);//Cad_Num($id);


	if (preg_match('"^ERROR:"',$escapada)) {

		return 'ERROR:inválido';
	}
	else {
		
		$sql="SELECT * FROM direcciones WHERE codigo='".$escapada."';";
		$result=mysql_query($sql,$Conexion);

		while($row = mysql_fetch_array($result)) {
			$url=$row["url"];
		}
		mysql_free_result($result);
		if ($url!=''){

			return $url;
		}
		else {
			return 'ERROR:noexiste';
		}
	}
}


// Busca una URL en la BD (definidas por el usuario)
function Buscar_por_id_propia($id){
	global $Conexion;

		$escapada= mysql_real_escape_string($id);

		$sql="SELECT * FROM elegidas WHERE id='".$escapada."';";
		$result=mysql_query($sql,$Conexion);

		while($row = mysql_fetch_array($result)) {
			$url=$row["url"];
		}
		mysql_free_result($result);
		if ($url!=''){

			return $url;
		}
		else {
			return 'ERROR:noexiste';
		}
}





// Guarda un log de accesos
function Logea($id){
	global $Conexion;
	$fecha= date("Y-m-d H:i:s");
	$refer= $_SERVER['HTTP_REFERER'];
	$sql="INSERT INTO log (idpag, fecha, refer) VALUES ('".$id."','".$fecha."','".$refer."');";
	$result=mysql_query($sql,$Conexion);
	$SQL_Error=  mysql_error();
	

}


// La redirección, propiamente dicha
function Redirecciona($url){
	header('Location: '. $url);
}



// Formulario para crear URLs
function Pantallacrea(){


	//Si no existe o no encuentra la plantilla, muestra una página por defecto
	if (!@include("plantilla_form.html")){
		echo '<html><head><title>Short URL</title></head><p>Introduzca la URL que desea acortar:</p><form action=""><input type="hidden" name="modo" value="new"><p>URL: <input type="text" name="url" value=""></p><p>Tambi&eacute;n puede elegir un mombre corto (ID)</p><p>ID: <input type="text" name="myid" value=""> (<em>opcional</em>)</p><input class="boton" type="submit" name="submit" value="Crear"></form><body></body></html>';
	}



}



// Esto es una exigencia del "cliente": Permite dar de alta direcciones sólo a IPs de la UGR (más o menos).
function FiltraIP(){

	/* $permitida = "150.214";
	
	$fragmentoIP= substr($_SERVER['REMOTE_ADDR'], 0, 7); 

	if ($fragmentoIP == $permitida){
		return true;
	}
	else {
		return flase;
	}*/
return true;
}




//Si todo ha ido bien, muestra el resultado en una página al efecto:
function Pantallamuestra($id){
	$url=$_GET["url"];
	$api=$_GET["api"];
	$callback=$_GET["callback"];
	$format=$_GET["format"];
	if($api>0 || ($format!="" && $format!="html")){
		$data = array ('url'=>"http://".DOMAIN."/$id",'target'=>$url);
		apiFormat($data,$api,$format,0,$callback);				
	}else{
		//Si no existe o no encuentra la plantilla, muestra una página por defecto
		if (!@include("plantilla_result.html")){
			echo '<html><head><title>Short URL</title></head><p>Se ha creado la URL corta '. $_SERVER['HTTP_HOST'] . '/' . $id  .'</p><body></body></html>';
		}
	}
}

 /**
 * Build A XML Data Set
 *
 * @param array $data Associative Array containing values to be parsed into an XML Data Set(s)
 * @param string $startElement Root Opening Tag, default fx_request
 * @param string $xml_version XML Version, default 1.0
 * @param string $xml_encoding XML Encoding, default UTF-8
 * @return string XML String containig values
 * @return mixed Boolean false on failure, string XML result on success
 */
function xml_encode($data, $startElement = 'request', $xml_version = '1.0', $xml_encoding = 'UTF-8'){
  if(!is_array($data)){
     $err = 'Invalid variable type supplied, expected array not found on line '.__LINE__." in Class: ".__CLASS__." Method: ".__METHOD__;
     trigger_error($err);
     if($this->_debug) echo $err;
     return false; //return false error occurred
  }
  $xml = new XmlWriter();
  $xml->openMemory();
  $xml->startDocument($xml_version, $xml_encoding);
  $xml->startElement($startElement);

  /**
   * Write XML as per Associative Array
   * @param object $xml XMLWriter Object
   * @param array $data Associative Data Array
   */
  function write(XMLWriter $xml, $data){
      foreach($data as $key => $value){
          if(is_array($value)){
              $xml->startElement($key);
              write($xml, $value);
              $xml->endElement();
              continue;
          }
          $xml->writeElement($key, $value);
      }
  }
  write($xml, $data);

  $xml->endElement();//write end element
  //Return the XML results
  return $xml->outputMemory(true); 
}



function apiFormat($data,$api,$format,$error,$callback){
	if($api==1 || $format=="text"){
		header('Content-type: text/plain');
		if($error){
			echo "ERROR: ".$data["error"];
		}else{
			echo $data["url"];
		}
	}else if($api==2 || $format=="json" ){
		$json= json_encode($data);
		$json=str_replace("\\/","/",$json);
		if($callback){
			header('Content-type: application/x-javascript');
			echo $callback."(".$json.");";
		}else{
			header('Content-type: application/json');
			echo $json;
		}	
	}else if($format=="xml" ){
		header('Content-type: text/xml');
		echo xml_encode($data);
	}	
}

//Under construction: Módulo de estadísticas de uso
function Estadistica($id){
	global $Conexion;

	$escapada= mysql_real_escape_string($id);

	$sql="SELECT * FROM log WHERE id='".$escapada."';";
	$result=mysql_query($sql,$Conexion);

	while($row = mysql_fetch_array($result)) {
		$Total++;
		$lista.= $row["url"];
	}

	mysql_free_result($result);
}


//Para crear una URL con un nombre asignado por el usuario
function CreaPropia($id,$url){
	global $Conexion;


	if (!TestURL($url)){
		$url= 'http://'. $url;
	}


	if ((strlen($id) > 4) && (strlen($id) < 51)){

		if (TestCaracteres($id)){

			$idescapada= mysql_real_escape_string($id);
			$urlescapada= mysql_real_escape_string($url);

			$sql="SELECT * FROM elegidas WHERE id='".$idescapada."';";
			$result=mysql_query($sql,$Conexion);

			while($row = mysql_fetch_array($result)) {
				$idb=$row["id"];
			}
			mysql_free_result($result);

			if ($idb != ''){
				return 'ERROR:existe';
			}
			else {

				$fecha= date("Y-m-d H:i:s");
				$sql="INSERT INTO elegidas (id, url, creada) VALUES ('".$idescapada."','".$urlescapada."','".$fecha."');";
				$result=mysql_query($sql,$Conexion);
				$SQL_Error=  mysql_error();
				if ($SQL_Error!=''){
					//echo $SQL_Error;
				}
				else {
					return $id;
				}
			
			}
		}
		else {
			return 'ERROR:caracteres';
		}
			
	}
	else{
		return 'ERROR:tamaño';
	}

}



// Pantallas de Errores
function MuestraError($Iderror){

	$Errores= array('ERROR:ip'=>'Lo siento. Me temo que estas URLs cortas s&oacute;lo se pueden dar de alta desde la red de la UGR (pero esto es s&oacute;lo para darlas de alta, no para usar la redirecci&oacute;n)','ERROR:autoreferencia'=>'No se puede llamar a una URL corta con una URL corta, es parad&oacute;jico. El universo conocido podr&iacute;a colapsar por autoreferencias como esta, y t&uacute; no quieres ser el culpable de algo como eso...','ERROR:tamaño' => 'La ID elegida como URL corta debe tener entre 5 y 50 caracteres (aunque 50 probablemente sea exagerar la definici&oacute;n de la palabra "corta")','ERROR:caracteres' => 'S&oacute;lo se admiten letras, n&uacute;meros y el signo "_". Pero las letras puden ser min&uacute;sculas y may&uacute;sculas, que ya es algo (en el futuro se admitir&aacute;n n&uacute;meros may&uacute;sculos, pero a&uacute;n no los hemos encontrado en este teclado)','ERROR:existe' => 'La cadena elegida como url corta ya existe (te han pillado la vez, prueba combinando min&uacute;sculas y may&uacute;sculas y esas cosas)','ERROR:noexiste' => 'No existe la URL corta elegida (es tu oportunidad para crearla, aprovecha la ocasi&oacute;n)','ERROR:vacío' =>'La url que quieres acortar está vacía... creo que eso es suficientemente corta :P','ERROR:inválido' => 'Código inválido: O sea, que has metido caracteres que este programa se niega a reconocer');

	$TextoError= $Errores[$Iderror];
	$url=$_GET["url"];
	$api=$_GET["api"];
	$format=$_GET["format"];
	$callback=$_GET["callback"];
	if($api>0 || ($format!="" && $format!="html")){
		$data = array ('url'=>null,'target'=>$url,'error'=>$TextoError);
		apiFormat($data,$api,$format,1,$callback);				
	}else{
		//Si no existe o no encuentra la plantilla, muestra una página por defecto
		if (!@include("plantilla_error.html")){
			echo '<html><head><title>Short URL</title></head>'. $TextoError .'<body></body></html>';
		}
	}
}

/**
 * Takes an URL and splits it up into it's protocol, site, and resource parts
 * 
 * Returns an associative array of protocol, port, site, and resource parts
 * Note: the URL does not have to have a protocol specified 
 * example:
 * <code>
 *     $url = "http://www.foo.com/blog/search?q=bar";
 *     $url_parts = getUrlParts($url);
 *     // $url_parts will contain the following:
 *     // Array
 *     // (
 *     //     [protocol] => http
 *     //     [port] =>
 *     //     [site] => www.foo.com
 *     //     [resource] => blog/search?q=bar
 *     // )
 *     
 *     $url = "www.foo.com:8080/blog/search?q=bar";
 *     $url_parts = getUrlParts($url);
 *     // $url_parts will contain the following:
 *     // Array
 *     // (
 *     //     [protocol] =>
 *     //     [port] => 8080
 *     //     [site] => www.foo.com
 *     //     [resource] => blog/search?q=bar
 *     // )
 * </code>
 * 
 * @param string	The URL that you want to split up
 * @return associative array 	Array containing the split up parts of the URL
 */
function getUrlParts($url) {
	$result = array();
	
	// Get the protocol, site and resource parts of the URL
	// original url = http://example.com/blog/index?name=foo
	// protocol = http://
	// site = example.com/
	// resource = blog/index?name=foo
	$regex = '#^(.*?//)*([\w\.\d]*)(:(\d+))*(/*)(.*)$#';
	$matches = array();
	preg_match($regex, $url, $matches);
			
	// Assign the matched parts of url to the result array
	$result['protocol'] = $matches[1];
	$result['port'] = $matches[4];
	$result['site'] = $matches[2];
	$result['resource'] = $matches[6];
	
	// clean up the site portion by removing the trailing /
	$result['site'] = preg_replace('#/$#', '', $result['site']);
	
	// clean up the protocol portion by removing the trailing ://
	$result['protocol'] = preg_replace('#://$#', '', $result['protocol']);
	
	return $result;
}

?>
